package com.sqbang.dbcompare.biz;

import com.github.houbb.word.checker.util.EnWordCheckers;
import com.sqbang.dbcompare.constant.CommonConst;
import com.sqbang.dbcompare.constant.Whether;
import com.sqbang.dbcompare.constant.enums.RegulationCheckTypeEnum;
import com.sqbang.dbcompare.pojo.bo.CheckReportBo;
import com.sqbang.dbcompare.pojo.bo.IndexBo;
import com.sqbang.dbcompare.pojo.cache.CommonData;
import com.sqbang.dbcompare.util.Inflector;
import com.sqbang.dbcompare.util.Tools;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.regex.Pattern;

/**
 * 用于检查的业务类
 * @author suqiongbang
 * @date 2021/11/4 22:34
 */
@Component
public class CheckBiz {
    /**
     * 用来缓存 单词的单数形式词
     */
    private static ConcurrentHashMap<String, String> singularizeMap = new ConcurrentHashMap<>();
    /**
     * 用来缓存 单词拼写检查
     */
    private static ConcurrentHashMap<String, List<String>> spellingMap = new ConcurrentHashMap<>();

    /**
     * 数字正则
     */
    Pattern numberCompile = Pattern.compile("[\\d]");

    /**
     * 检查单词的单数形式的词，如果没有就返回本身
     * @return
     */
    public void checkPluralizeWord(String wordOfLowerCase, Map<String, List<CheckReportBo.Suggest>> resultMap, String key){
        String result = singularizeMap.get(wordOfLowerCase);
        if (result == null) {
            result = wordOfLowerCase;
            String pluralize = Inflector.getInstance().pluralize(wordOfLowerCase);
            // 先找是否有复数，如果复数跟自己相同，则有可能是个复数词，继续查
            if (pluralize.equals(wordOfLowerCase)) {
                result = Inflector.getInstance().singularize(wordOfLowerCase);
            }
            singularizeMap.put(wordOfLowerCase, result);
        }
        if (!result.equals(wordOfLowerCase)) {
            this.add2Map(resultMap, key, RegulationCheckTypeEnum.TABLE_NAME_CONTAIN_PLURALITY, Arrays.asList(wordOfLowerCase, result));
        }
    }

    /**
     * 检查名称
     * @param str 名称字符串
     * @param key map里面的key
     * @param checkIds 检查的枚举code
     * @param resultMap 结果map
     */
    public void checkWord(String str, String key, String checkIds, Map<String, List<CheckReportBo.Suggest>> resultMap){
        // 检查小写
        if (StringUtils.isEmpty(checkIds) || checkIds.contains(RegulationCheckTypeEnum.NAME_CONTAIN_UPPER_CASE.getCode())) {
            if (Tools.isContainUpperCase(str)){
                this.add2Map(resultMap, key, RegulationCheckTypeEnum.NAME_CONTAIN_UPPER_CASE);
            }
        }
        // 不能数字开头
        if (StringUtils.isEmpty(checkIds) || checkIds.contains(RegulationCheckTypeEnum.NAME_DIGIT_START.getCode())) {
            if (str.contains(CommonConst.SYMBOL_UNDERLINE)) {
                String[] split = str.split(CommonConst.SYMBOL_UNDERLINE);
                for (String name : split) {
                    if (Tools.isStartWithNumber(name)){
                        this.add2Map(resultMap, key, RegulationCheckTypeEnum.NAME_DIGIT_START, Arrays.asList(name));
                        break;
                    }
                }
            } else if (Tools.isStartWithNumber(str)){
                this.add2Map(resultMap, key, RegulationCheckTypeEnum.NAME_DIGIT_START, Arrays.asList(str));
            }
        }
        // 禁用保留字
        if (StringUtils.isEmpty(checkIds) || checkIds.contains(RegulationCheckTypeEnum.NAME_USE_KEYWORD.getCode())) {
            if (Tools.isReservedWord(str.toLowerCase(Locale.ROOT))){
                this.add2Map(resultMap, key, RegulationCheckTypeEnum.NAME_USE_KEYWORD);
            }
        }
    }

    /**
     * 检查单词拼写，多线程[暂未使用，后续完善]
     */
    @Deprecated
    public void checkWordSpelling(Map<String, List<String>> wordMap){
        for (String word : wordMap.keySet()) {
            FutureTask<List<String>> futureTask = new FutureTask<>(new WordTask (word));
            try {
                wordMap.put(word, futureTask.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 检查单词拼写，单线程
     */
    public void checkWordSpelling2(Map<String, Set<String>> wordMap, Map<String, List<CheckReportBo.Suggest>> resultMap){
        for (Map.Entry<String, Set<String>> entry : wordMap.entrySet()) {
            if (!CollectionUtils.isEmpty(entry.getValue())) {
                for (String word : entry.getValue()) {
                    if (!CommonData.ignoreSpellingWordStr.contains(word)) {
                        List<String> suggestList = spellingMap.get(word);
                        String newWord = word;
                        if (CollectionUtils.isEmpty(suggestList)) {
                            // 去掉单词中的数字
                            String word2 = numberCompile.matcher(word).replaceAll("");
                            if (word2.length() > 1) {
                                newWord = word2;
                                suggestList = EnWordCheckers.correctList(word2);
                            } else {
                                suggestList = new ArrayList<>();
                                suggestList.add(word);
                            }
                            spellingMap.put(word, suggestList);
                        }
                        if (!CollectionUtils.isEmpty(suggestList) && (suggestList.size() > 1 || !suggestList.get(0).equals(newWord))) {
                            this.add2Map(resultMap, entry.getKey(), RegulationCheckTypeEnum.NAME_ERROR_SPELL, Arrays.asList(word, suggestList.toString()));
                        }
                    }
                }
            }
        }
    }

    /**
     * 收集单词，放到wordMap中
     * @param key
     * @param nameOfLowerCase
     * @param wordMap
     */
    public void collectWord(String key, String nameOfLowerCase, Map<String, Set<String>> wordMap){
        if (nameOfLowerCase.contains(CommonConst.SYMBOL_UNDERLINE)) {
            String[] split = nameOfLowerCase.split(CommonConst.SYMBOL_UNDERLINE);
            for (String name : split) {
                this.add2Map2(wordMap, key, name);
            }
        } else {
            this.add2Map2(wordMap, key, nameOfLowerCase);
        }
    }

    /**
     * 将 表名或字段名及其建议 添加到map里面
     */
    public void add2Map(Map<String, List<CheckReportBo.Suggest>> resultMap, String name, RegulationCheckTypeEnum suggestEnum){
        this.add2Map(resultMap, name, suggestEnum, null);
    }
    public void add2Map(Map<String, List<CheckReportBo.Suggest>> resultMap, String name, RegulationCheckTypeEnum suggestEnum, List<String> suggestParamList){
        String suggestDesc = CollectionUtils.isEmpty(suggestParamList) ? suggestEnum.getDesc() : String.format(suggestEnum.getDesc(), suggestParamList.toArray());
        CheckReportBo.Suggest suggest = RegulationCheckTypeEnum.NAME_ERROR_SPELL.getCode().equals(suggestEnum.getCode())
                ? new CheckReportBo.Suggest(suggestEnum.getCode(), suggestDesc, suggestParamList.get(0)) : new CheckReportBo.Suggest(suggestEnum.getCode(), suggestDesc);
        new CheckReportBo.Suggest(suggestEnum.getCode(), suggestDesc);
        if (resultMap.containsKey(name)) {
            resultMap.get(name).add(suggest);
        } else {
            List<CheckReportBo.Suggest> suggestList = new ArrayList<>();
            suggestList.add(suggest);
            resultMap.put(name, suggestList);
        }
    }

    /**
     * 将 表名或字段名及其建议 添加到map里面
     */
    public void add2Map2(Map<String, Set<String>> wordMap, String key, String str){
        if (wordMap.containsKey(key)) {
            wordMap.get(key).add(str);
        } else {
            Set<String> set = new HashSet<>();
            set.add(str);
            wordMap.put(key, set);
        }
    }

    /**
     * 检验索引名称
     */
    public void checkIndexName(String tableName, Map<String, List<CheckReportBo.Suggest>> resultMap, LinkedHashMap<String, IndexBo> indexMap) {
        if (!CollectionUtils.isEmpty(indexMap)) {
            for (Map.Entry<String, IndexBo> entry : indexMap.entrySet()) {
                if (!CommonConst.WORD_PRIMARY.equals(entry.getKey().toUpperCase())){
                    IndexBo indexBo = entry.getValue();
                    String keyName = indexBo.getIndexName().toLowerCase();
                    if (Whether.NO.equals(indexBo.getNonUnique()) && keyName.indexOf("uk_") != 0){
                        this.add2Map(resultMap, tableName, RegulationCheckTypeEnum.INDEX_NAME_ERROR, Arrays.asList(indexBo.getIndexName()));
                    } else if (keyName.indexOf("idx_") != 0) {
                        this.add2Map(resultMap, tableName, RegulationCheckTypeEnum.INDEX_NAME_ERROR, Arrays.asList(indexBo.getIndexName()));
                    }
                }
            }
        }
    }

    static class WordTask implements Callable<List<String>> {
        String word;
        public WordTask(){}
        public WordTask(String word) {
            this.word = word;
        }
        @Override
        public List<String> call() throws Exception {
            return EnWordCheckers.correctList(word);
        }
    }
}
